{{>partial_header}}

use reqwest;

use crate::apis::ResponseContent;
use super::{Error, configuration};

{{#operations}}
{{#operation}}
{{#vendorExtensions.x-group-parameters}}
{{#allParams}}
{{#-first}}
/// struct for passing parameters to the method `{{operationId}}`
#[derive(Clone, Debug)]
pub struct {{{operationIdCamelCase}}}Params {
{{/-first}}
    {{#description}}
    /// {{{.}}}
    {{/description}}
    pub {{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}String{{/isString}}{{#isUuid}}String{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}crate::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{#hasMore}},{{/hasMore}}
{{#-last}}
}

{{/-last}}
{{/allParams}}
{{/vendorExtensions.x-group-parameters}}
{{/operation}}
{{/operations}}

{{#supportMultipleResponses}}
{{#operations}}
{{#operation}}
/// struct for typed successes of method `{{operationId}}`
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum {{{operationIdCamelCase}}}Success {
    {{#responses}}
    {{#is2xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is2xx}}
    {{#is3xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is3xx}}
    {{/responses}}
    UnknownValue(serde_json::Value),
}

{{/operation}}
{{/operations}}
{{/supportMultipleResponses}}
{{#operations}}
{{#operation}}
/// struct for typed errors of method `{{operationId}}`
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum {{{operationIdCamelCase}}}Error {
    {{#responses}}
    {{#is4xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is4xx}}
    {{#is5xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is5xx}}
    {{#isDefault}}
    DefaultResponse({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/isDefault}}
    {{/responses}}
    UnknownValue(serde_json::Value),
}

{{/operation}}
{{/operations}}

{{#operations}}
{{#operation}}
{{#description}}
/// {{{.}}}
{{/description}}
{{#notes}}
/// {{{.}}}
{{/notes}}
{{#vendorExtensions.x-group-parameters}}
pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: &configuration::Configuration{{#allParams}}{{#-first}}, params: {{{operationIdCamelCase}}}Params{{/-first}}{{/allParams}}) -> Result<{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{#returnType}}{{{returnType}}}{{/returnType}}{{/supportMultipleResponses}}, Error<{{{operationIdCamelCase}}}Error>> {
    // unbox the parameters
    {{#allParams}}
    let {{paramName}} = params.{{paramName}};
    {{/allParams}}

{{/vendorExtensions.x-group-parameters}}
{{^vendorExtensions.x-group-parameters}}
pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: &configuration::Configuration, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}&str{{/isString}}{{#isUuid}}&str{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}crate::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> Result<{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{#returnType}}{{{returnType}}}{{/returnType}}{{/supportMultipleResponses}}, Error<{{{operationIdCamelCase}}}Error>> {
{{/vendorExtensions.x-group-parameters}}

    let client = &configuration.client;

    let uri_str = format!("{}{{{path}}}", configuration.base_path{{#pathParams}}, {{{baseName}}}={{#isString}}crate::apis::urlencode({{/isString}}{{{paramName}}}{{^required}}.unwrap(){{/required}}{{#required}}{{#isNullable}}.unwrap(){{/isNullable}}{{/required}}{{#isListContainer}}.join(",").as_ref(){{/isListContainer}}{{#isString}}){{/isString}}{{/pathParams}});
    let mut req_builder = client.{{{httpMethod}}}(uri_str.as_str());

    {{#queryParams}}
    {{#required}}
    req_builder = req_builder.query(&[("{{{baseName}}}", &{{{paramName}}}{{#isListContainer}}.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","){{/isListContainer}}.to_string())]);
    {{/required}}
    {{^required}}
    if let Some(ref s) = {{{paramName}}} {
        req_builder = req_builder.query(&[("{{{baseName}}}", &s{{#isListContainer}}.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","){{/isListContainer}}.to_string())]);
    }
    {{/required}}
    {{/queryParams}}
    {{#hasAuthMethods}}
    {{#authMethods}}
    {{#isApiKey}}
    {{#isKeyInQuery}}
    if let Some(ref apikey) = configuration.api_key {
        let key = apikey.key.clone();
        let val = match apikey.prefix {
            Some(ref prefix) => format!("{} {}", prefix, key),
            None => key,
        };
        req_builder = req_builder.query(&[("{{{keyParamName}}}", val)]);
    }
    {{/isKeyInQuery}}
    {{/isApiKey}}
    {{/authMethods}}
    {{/hasAuthMethods}}
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    {{#hasHeaderParams}}
    {{#headerParams}}
    {{#required}}
    {{^isNullable}}
    req_builder = req_builder.header("{{{baseName}}}", {{{paramName}}}{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
    {{/isNullable}}
    {{#isNullable}}
    match {{{paramName}}} {
        Some(param_value) => { req_builder = req_builder.header("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string()); },
        None => { req_builder = req_builder.header("{{{baseName}}}", ""); },
    }
    {{/isNullable}}
    {{/required}}
    {{^required}}
    if let Some(param_value) = {{{paramName}}} {
        req_builder = req_builder.header("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
    }
    {{/required}}
    {{/headerParams}}
    {{/hasHeaderParams}}
    {{#hasAuthMethods}}
    {{#authMethods}}
    {{#isApiKey}}
    {{#isKeyInHeader}}
    if let Some(ref apikey) = configuration.api_key {
        let key = apikey.key.clone();
        let val = match apikey.prefix {
            Some(ref prefix) => format!("{} {}", prefix, key),
            None => key,
        };
        req_builder = req_builder.header("{{{keyParamName}}}", val);
    };
    {{/isKeyInHeader}}
    {{/isApiKey}}
    {{#isBasic}}
    {{#isBasicBasic}}
    if let Some(ref auth_conf) = configuration.basic_auth {
        req_builder = req_builder.basic_auth(auth_conf.0.to_owned(), auth_conf.1.to_owned());
    };
    {{/isBasicBasic}}
    {{#isBasicBearer}}
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };
    {{/isBasicBearer}}
    {{/isBasic}}
    {{#isOAuth}}
    if let Some(ref token) = configuration.oauth_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };
    {{/isOAuth}}
    {{/authMethods}}
    {{/hasAuthMethods}}
    {{#isMultipart}}
    {{#hasFormParams}}
    let mut form = reqwest::multipart::Form::new();
    {{#formParams}}
    {{#isFile}}
    {{^supportAsync}}
    {{#required}}
    {{^isNullable}}
    form = form.file("{{{baseName}}}", {{{paramName}}})?;
    {{/isNullable}}
    {{#isNullable}}
    match {{{paramName}}} {
        Some(param_value) => { form = form.file("{{{baseName}}}", param_value)?; },
        None => { unimplemented!("Required nullable form file param not supported"); },
    }
    {{/isNullable}}
    {{/required}}
    {{^required}}
    if let Some(param_value) = {{{paramName}}} {
        form = form.file("{{{baseName}}}", param_value)?;
    }
    {{/required}}
    {{/supportAsync}}
    {{#supportAsync}}
    // TODO: support file upload for '{{{baseName}}}' parameter
    {{/supportAsync}}
    {{/isFile}}
    {{^isFile}}
    {{#required}}
    {{^isNullable}}
    form = form.text("{{{baseName}}}", {{{paramName}}}{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
    {{/isNullable}}
    {{#isNullable}}
    match {{{paramName}}} {
        Some(param_value) => { form = form.text("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string()); },
        None => { form = form.text("{{{baseName}}}", ""); },
    }
    {{/isNullable}}
    {{/required}}
    {{^required}}
    if let Some(param_value) = {{{paramName}}} {
        form = form.text("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
    }
    {{/required}}
    {{/isFile}}
    {{/formParams}}
    req_builder = req_builder.multipart(form);
    {{/hasFormParams}}
    {{/isMultipart}}
    {{^isMultipart}}
    {{#hasFormParams}}
    let mut form_params = std::collections::HashMap::new();
    {{#formParams}}
    {{#isFile}}
    {{#required}}
    {{^isNullable}}
    form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content"));
    {{/isNullable}}
    {{#isNullable}}
    match {{{paramName}}} {
        Some(param_value) => { form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content")); },
        None => { unimplemented!("Required nullable file form param not supported with x-www-form-urlencoded content"); },
    }
    {{/isNullable}}
    {{/required}}
    {{^required}}
    if let Some(param_value) = {{{paramName}}} {
        form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content"));
    }
    {{/required}}
    {{/isFile}}
    {{^isFile}}
    {{#required}}
    {{^isNullable}}
    form_params.insert("{{{baseName}}}", {{{paramName}}}{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
    {{/isNullable}}
    {{#isNullable}}
    match {{{paramName}}} {
        Some(param_value) => { form_params.insert("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string()); },
        None => { form_params.insert("{{{baseName}}}", ""); },
    }
    {{/isNullable}}
    {{/required}}
    {{^required}}
    if let Some(param_value) = {{{paramName}}} {
        form_params.insert("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
    }
    {{/required}}
    {{/isFile}}
    {{/formParams}}
    req_builder = req_builder.form(&form_params);
    {{/hasFormParams}}
    {{/isMultipart}}
    {{#hasBodyParam}}
    {{#bodyParams}}
    req_builder = req_builder.json(&{{{paramName}}});
    {{/bodyParams}}
    {{/hasBodyParam}}

    let req = req_builder.build()?;
    let {{^supportAsync}}mut {{/supportAsync}}resp = client.execute(req){{#supportAsync}}.await{{/supportAsync}}?;

    let status = resp.status();
    let content = resp.text(){{#supportAsync}}.await{{/supportAsync}}?;

    if status.is_success() {
        {{^supportMultipleResponses}}
        {{^returnType}}
        Ok(())
        {{/returnType}}
        {{#returnType}}
        serde_json::from_str(&content).map_err(Error::from)
        {{/returnType}}
        {{/supportMultipleResponses}}
        {{#supportMultipleResponses}}
        let entity: Option<{{{operationIdCamelCase}}}Success> = serde_json::from_str(&content).ok();
        let result = ResponseContent { status, content, entity };
        Ok(result)
        {{/supportMultipleResponses}}
    } else {
        let entity: Option<{{{operationIdCamelCase}}}Error> = serde_json::from_str(&content).ok();
        let error = ResponseContent { status, content, entity };
        Err(Error::ResponseError(error))
    }
}

{{/operation}}
{{/operations}}
